package com.lge.pitchingcoins.util.sessions;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;

import android.content.Context;

import com.lge.pitchingcoins.ui.server.layer.GamePlayForServerLayer;
import com.lge.pitchingcoins.util.PitchingCoinsLog;
import com.lge.pitchingcoins.util.Util;

public class ServerSession {
    public static String toAllString = "all+*!%!%";

    private Context mContext = null;
    private GamePlayForServerLayer callback = null;
    private ServerSocket serverSocket = null;
    
    private int playerTurnIndex = 0;

    private ArrayList<ClientsInfo> clientInfoList = null;

    public ServerSession(Context context) {
        mContext = context;

        clientInfoList = new ArrayList<ClientsInfo>();

        CreateSocketThread thread = new CreateSocketThread();
        thread.start();
    }


    // Getters and Setters
    public ClientsInfo getClientInfoListWith(int position) {
        return clientInfoList.get(position);
    }

    public GamePlayForServerLayer getCallback() {
        return callback;
    }

    public void setCallback(GamePlayForServerLayer callback) {
        this.callback = callback;
    }


    // Member Methods
    public void sendMessageTo(String message, String toNickname) {
        Util.lockWithWIFI(mContext);

        for (ClientsInfo client : clientInfoList) {
            if (toNickname.equals(ServerSession.toAllString)) {
                client.sendMessage(message);
            } else {
                if (client.getNickname() != null && client.getNickname().equals(toNickname)) {
                    client.sendMessage(message);
                    break;
                }
            }
        }
        
        Util.unlockWithWIFI(mContext);
    }

    public void leaveClient(String nickname) {
        ClientsInfo toRemove = null;

        for (ClientsInfo client : clientInfoList) {
            if (client.getNickname() != null && client.getNickname().equals(nickname)) {
                toRemove = client;
                break;
            }
        }

        if (toRemove != null) {
            clientInfoList.remove(toRemove);
            
            if (callback != null) {
                callback.onClientEnteredOrLeave();
            }
            
            PitchingCoinsLog.d(nickname + " is leaving!!");
        } else {
            PitchingCoinsLog.d("No one leaves");
        }
    }

    public void tearDown() {
        if (serverSocket != null && !serverSocket.isClosed()) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public int numberOfClients() {
        return clientInfoList.size();
    }
    
    public String getTurnedPlayerName() {
        String clientName = "";
        
        if (playerTurnIndex < numberOfClients()) {
            clientName = clientInfoList.get(playerTurnIndex++).getNickname();
        } else {
            clientName = "";
            playerTurnIndex = 0;
        }
        
        return clientName;
    }
    
    public int getTagFromPlayerName(String name) {
        for (int i=0; i<numberOfClients(); i++) {
            if (clientInfoList.get(i).getNickname().equals(name)) {
                return i;
            }
        }
        
        return -1;
    }
    
    public String getPlayerNameWithTag(int tag) {
        return clientInfoList.get(tag).getNickname();
    }


    class CreateSocketThread extends Thread {
        @Override
        public void run() {
            try {
                serverSocket = new ServerSocket(Util.PC_PORT);

                while (true) {
                    Socket clientSocket = serverSocket.accept();
                    PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());

                    ClientsInfo client = new ClientsInfo(clientSocket, writer);
                    clientInfoList.add(client);

                    // Send a message to know what the client's nickname is
                    client.sendMessage("WRU");

                    Thread thread = new Thread(new ClientHandler(client));
                    thread.start();
                }
            } catch (SocketException e) {
                PitchingCoinsLog.d("Server socket is closed. So accepting socket is finished!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ClientHandler implements Runnable {
        private BufferedReader reader = null;
        private Socket socket = null;
        private ClientsInfo thisClient = null;

        public ClientHandler(ClientsInfo client) {
            try {
                thisClient = client;

                socket = client.getSocket();
                InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
                reader = new BufferedReader(inputStreamReader);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            String message;

            try {
                Util.lockWithWIFI(mContext);

                while ((message = reader.readLine()) != null) {
                    if (message.substring(0, 2).equals("IM")) {
                        thisClient.setNickname(message.substring(3));

                        // TODO If the nickname is already exist?
                        PitchingCoinsLog.d("I will send \": " + message + "\" to " + thisClient.getNickname());
                        sendMessageTo(message, thisClient.getNickname());

                        if (callback != null) {
                            callback.onClientEnteredOrLeave();
                        }
                    } else if (message.substring(0, 2).equals("IL")) {
                        leaveClient(message.substring(3));
                    } else {
                        if (callback != null) {
                            callback.onMessageReceived(message, thisClient.getNickname());
                        }
                    }
                }
                
                Util.unlockWithWIFI(mContext);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}